"""Support for OpenWRT (luci) routers."""
import asyncio
import logging
from typing import Any, Dict
from datetime import timedelta

from openwrt_luci_rpc.openwrt_luci_rpc import OpenWrtLuciRPC  # pylint: disable=import-error
from openwrt_luci_rpc.utilities import normalise_keys  # pylint: disable=import-error
from openwrt_luci_rpc.constants import Constants  # pylint: disable=import-error
from openwrt_luci_rpc.exceptions import LuciConfigError, InvalidLuciTokenError  # pylint: disable=import-error

from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntry

from homeassistant.const import (  # pylint: disable=import-error
    Platform,
    CONF_HOST,
    CONF_PASSWORD,
    CONF_SSL,
    CONF_USERNAME,
    CONF_VERIFY_SSL,
    CONF_NAME,
    CONF_MAC,
)
import homeassistant.helpers.config_validation as cv  # pylint: disable=import-error

from homeassistant.helpers.dispatcher import (  # pylint: disable=import-error
    async_dispatcher_send,
)

from .const import (
    DOMAIN,
    CONF_RPC,
    CONF_BLACKLIST,
    SIGNAL_STATE_UPDATED,
)

_LOGGER = logging.getLogger(__name__)
PLATFORMS = [Platform.SWITCH]


async def handle_openwrt_rule(hass: HomeAssistant, rpc: OpenWrtLuciRPC,
                              config_rules: Dict[str, Any] | None = None) -> bool:
    if config_rules is None:
        return False

    _LOGGER.debug("parental_control config_rules =====> %s", config_rules)
    rule_key_List = [rule[CONF_NAME] for rule in config_rules]
    _LOGGER.debug("parental_control rule_key_List =====> %s", rule_key_List)

    parentcontrol_result = await hass.async_add_executor_job(rpc.rpc_call, 'get_all', 'parentcontrol')

    # 在当前OpenWrt路由器中，筛选出已存在的，name开头为 "ha_" 的parentcontrol rule.
    ha_parentcontrol_rules = [
        rule_entry['harulekey']
        for rule_entry in parentcontrol_result.values()
        if 'mac' in rule_entry and 'harulekey' in rule_entry and rule_entry['harulekey'].startswith('ha_')
    ]

    _LOGGER.debug("parental_control ha_parentcontrol_rules =====> %s",
                  ha_parentcontrol_rules)

    delete_rules = [
        rule_name
        for rule_name in ha_parentcontrol_rules
        if rule_name not in rule_key_List
    ]

    _LOGGER.debug("parental_control __init__  delete_rules =====> %s",
                  delete_rules)

    new_rules = [
        rule_name
        for rule_name in rule_key_List
        if rule_name not in ha_parentcontrol_rules
    ]

    _LOGGER.debug("parental_control __init__  new_rules =====> %s",
                  new_rules)

    # 已删除的MAC地址，从OpenWrt中删除；
    if delete_rules is not None:
        for key in delete_rules:
            for cfg_name, rule_entry in parentcontrol_result.items():
                if 'mac' in rule_entry and 'harulekey' in rule_entry and rule_entry['harulekey'] == key:
                    _LOGGER.info("parentcontrol rule %s is deleting.", key)
                    await hass.async_add_executor_job(rpc.rpc_call, "delete", "parentcontrol", cfg_name)
                    await hass.async_add_executor_job(rpc.rpc_call, "commit", "parentcontrol")

    if new_rules is not None:
        for rule in config_rules:
            key = rule[CONF_NAME]
            if key in new_rules:
                _LOGGER.info("OpenWrt Luci: add new parentcontrol rule: %s", key)
                cfg_name = await hass.async_add_executor_job(rpc.rpc_call, "add", "parentcontrol", "time")
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "mac", rule[CONF_MAC])
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "enable", "0")
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "harulekey", key)
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "remarks", key)
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "timeend", "00:00")
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "timestart", "00:00")
                await hass.async_add_executor_job(rpc.rpc_call, "set", "parentcontrol", cfg_name, "week", "*")
                await hass.async_add_executor_job(rpc.rpc_call, "commit", "parentcontrol")

    await hass.async_add_executor_job(rpc.rpc_call, "apply", "True")
    await hass.async_add_executor_job(rpc.rpc_sys_call, "exec", "/etc/init.d/parentcontrol restart")

    return True


async def async_setup(hass: HomeAssistant, config: ConfigEntry) -> bool:
    """Set up the Custom component from yaml configuration."""
    hass.data.setdefault(DOMAIN, {})
    return True


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    _LOGGER.info("Set up parental_control platform from a ConfigEntry")
    _LOGGER.debug("parental_control ConfigEntry data =====> %s",
                  dict(config_entry.data))
    _LOGGER.debug(
        "__init__.async_setup_entry: config_entry.entry_id => %s", config_entry.entry_id)
    _LOGGER.debug(
        "__init__.async_setup_entry: config_entry.unique_id => %s", config_entry.unique_id)
    _LOGGER.debug("__init__.async_setup_entry:  config_entry.data => %s",
                  dict(config_entry.data))
    _LOGGER.debug("__init__.async_setup_entry:  config_entry.options => %s",
                  dict(config_entry.options))

    """Set up platform from a ConfigEntry."""
    hass.data.setdefault(DOMAIN, {})
    hass_data = dict(config_entry.data)

    if not config_entry.unique_id:
        hass.config_entries.async_update_entry(
            config_entry, unique_id=config_entry.title)

    if CONF_RPC in hass_data:
        _rpc = hass_data[CONF_RPC]
    else:
        _rpc = await hass.async_add_executor_job(LuciRPC, config_entry.data)
        if not _rpc.success_init:
            return False

        hass_data[CONF_RPC] = _rpc

    # 注册更新侦听器以便于在选项更新时，更新配置项。
    unsub_options_update_listener = config_entry.add_update_listener(
        options_update_listener)
    # 存储对unsubscribe函数的引用，以便在删除实体时进行清理。
    hass_data["unsub_options_update_listener "] = unsub_options_update_listener

    await handle_openwrt_rule(hass, _rpc, hass_data['blacklist'])

    hass.data[DOMAIN][config_entry.entry_id] = hass_data

    # Forward the setup to the switch platform.
    for component in PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, component)
        )

    # async_dispatcher_send(hass, SIGNAL_STATE_UPDATED)

    return True


async def options_update_listener(hass: HomeAssistant, config_entry: ConfigEntry):
    """Handle options update."""
    _LOGGER.debug("Handle options update.")
    _LOGGER.debug(
        "Handle options update. config_entry.entry_id => %s", config_entry.entry_id)
    _LOGGER.debug("Handle options update. config_entry.data => %s",
                  dict(config_entry.data))
    _LOGGER.debug("Handle options update. config_entry.options => %s",
                  dict(config_entry.options))

    await hass.config_entries.async_reload(config_entry.entry_id)


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""
    _LOGGER.debug("Unloading parental_control")
    _LOGGER.debug(
        "Unloading parental_control. config_entry.entry_id => %s", entry.entry_id)
    _LOGGER.debug(
        "Unloading parental_control. config_entry.data => %s", dict(entry.data))
    _LOGGER.debug(
        "Unloading parental_control. config_entry.options => %s", dict(entry.options))

    if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
        # Remove config entry from domain.
        entry_data = hass.data[DOMAIN].pop(entry.entry_id)
        # Remove options_update_listener.
        entry_data["unsub_options_update_listener "]()

    return unload_ok


class LuciConfig():

    def __init__(self, name, desc, test_key, values, file):
        self.name = name
        self.desc = desc
        self.test_key = test_key.split(",")
        self.values = values
        self.file = file

    def __repr__(self):
        return self.name

    def __eq__(self, other):
        if isinstance(other, LuciConfig):
            return (self.name == other.name)
        else:
            return False

    def __ne__(self, other):
        return (not self.__eq__(other))

    def __hash__(self):
        return hash(self.__repr__())


class LuciConfigItem():

    def __init__(self):
        self.id = ""
        self.mac = ""
        self.name = ""
        self.enabled = False

    def __repr__(self):
        return self.name

    def __eq__(self, other):
        if isinstance(other, LuciConfigItem):
            return (self.id == other.id)
        else:
            return False

    def __ne__(self, other):
        return (not self.__eq__(other))

    def __hash__(self):
        return hash(self.__repr__())


class LuciRPC():
    def __init__(self, config):
        """Initialize the router."""
        self._rpc = OpenWrtLuciRPC(
            config.get(CONF_HOST),
            config.get(CONF_USERNAME),
            config.get(CONF_PASSWORD),
            config.get(CONF_SSL),
            config.get(CONF_VERIFY_SSL),
        )
        self.host = config.get(CONF_HOST)
        self.success_init = self._rpc.token is not None
        if not self.success_init:
            _LOGGER.error("Cannot connect to luci")
            return

        self.rule = {}

    def rpc_call(self, method, *args, **kwargs):
        rpc_uci_call = Constants.LUCI_RPC_UCI_PATH.format(
            self._rpc.host_api_url), method, *args
        try:
            _LOGGER.debug("rpc_uci_call ==> %s ", rpc_uci_call)
            rpc_result = self._rpc._call_json_rpc(*rpc_uci_call)
        except InvalidLuciTokenError:
            _LOGGER.info("Refreshing login token")
            self._rpc._refresh_token()
            return self.rpc_call(method, args)

        return rpc_result

    def rpc_sys_call(self, method, *args, **kwargs):
        rpc_uci_call = Constants.LUCI_RPC_SYS_PATH.format(
            self._rpc.host_api_url), method, *args
        try:
            _LOGGER.debug("rpc_uci_call ==> %s ", rpc_uci_call)
            rpc_result = self._rpc._call_json_rpc(*rpc_uci_call)
        except InvalidLuciTokenError:
            _LOGGER.info("Refreshing login token")
            self._rpc._refresh_token()
            return self.rpc_call(method, args)

        return rpc_result
